using Xunit;

namespace CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests.BindablePropertyAttributeSourceGeneratorTests;

public class EdgeCaseTests : BaseBindablePropertyAttributeSourceGeneratorTest
{
	[Fact]
	public async Task GenerateBindableProperty_PropertyWithReservedKeywords_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
            using CommunityToolkit.Maui;
            using Microsoft.Maui.Controls;

            namespace {{defaultTestNamespace}};

            public partial class {{defaultTestClassName}} : View
            {
                [BindableProperty]
                public partial string @class { get; set; }

                [BindableProperty]
                public partial string @namespace { get; set; }
            }
            """;

		const string expectedGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
            // <auto-generated>
            // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
            #pragma warning disable
            #nullable enable
            namespace {{defaultTestNamespace}};
            public partial class {{defaultTestClassName}}
            {
                /// <summary>
                /// Backing BindableProperty for the <see cref = "@class"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty classProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("@class", typeof(string), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial string @class { get => (string)GetValue(classProperty); set => SetValue(classProperty, value); }

                /// <summary>
                /// Backing BindableProperty for the <see cref = "@namespace"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty namespaceProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("@namespace", typeof(string), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial string @namespace { get => (string)GetValue(namespaceProperty); set => SetValue(namespaceProperty, value); }
            }
            """;

		await VerifySourceGeneratorAsync(source, expectedGenerated);
	}

	[Fact]
	public async Task GenerateBindableProperty_PropertyIsByteEnum_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
            using CommunityToolkit.Maui;
            using Microsoft.Maui.Controls;

            namespace {{defaultTestNamespace}};

            public partial class {{defaultTestClassName}} : View
            {
                [BindableProperty(DefaultValue = Status.Approved)]
                public partial Status InvoiceStatus { get; set; }
            }
            
            public enum Status : byte
            {
                Pending = 0,
                Approved = 1,
                Rejected = 2
            }
            """;

		const string expectedGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
            // <auto-generated>
            // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
            #pragma warning disable
            #nullable enable
            namespace {{defaultTestNamespace}};
            public partial class {{defaultTestClassName}}
            {
                /// <summary>
                /// Backing BindableProperty for the <see cref = "InvoiceStatus"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty InvoiceStatusProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("InvoiceStatus", typeof(TestNamespace.Status), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), (TestNamespace.Status)1, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial TestNamespace.Status InvoiceStatus { get => (TestNamespace.Status)GetValue(InvoiceStatusProperty); set => SetValue(InvoiceStatusProperty, value); }
            }
            """;

		await VerifySourceGeneratorAsync(source, expectedGenerated);
	}

	[Fact]
	public async Task GenerateBindableProperty_PropertyIsLongEnum_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
            using CommunityToolkit.Maui;
            using Microsoft.Maui.Controls;

            namespace {{defaultTestNamespace}};

            public partial class {{defaultTestClassName}} : View
            {
                [BindableProperty(DefaultValue = Status.Rejected)]
                public partial Status InvoiceStatus { get; set; }
            }
            
            public enum Status : long
            {
                Pending = 0,
                Approved = 1,
                Rejected = long.MaxValue
            }
            """;

		const string expectedGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
            // <auto-generated>
            // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
            #pragma warning disable
            #nullable enable
            namespace {{defaultTestNamespace}};
            public partial class {{defaultTestClassName}}
            {
                /// <summary>
                /// Backing BindableProperty for the <see cref = "InvoiceStatus"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty InvoiceStatusProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("InvoiceStatus", typeof(TestNamespace.Status), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), (TestNamespace.Status)9223372036854775807, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial TestNamespace.Status InvoiceStatus { get => (TestNamespace.Status)GetValue(InvoiceStatusProperty); set => SetValue(InvoiceStatusProperty, value); }
            }
            """;

		await VerifySourceGeneratorAsync(source, expectedGenerated);
	}

	[Fact]
	public async Task GenerateBindableProperty_NullableValueTypes_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""			
            using System;
            using CommunityToolkit.Maui;
            using Microsoft.Maui.Controls;

            namespace {{defaultTestNamespace}};

            public partial class {{defaultTestClassName}} : View
            {
                [BindableProperty]
                public partial int? NullableInt { get; set; }

                [BindableProperty]
                public partial DateTime? NullableDateTime { get; set; }

                [BindableProperty]
                public partial bool? NullableBool { get; set; }
            }
            """;

		const string expectedGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
            // <auto-generated>
            // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
            #pragma warning disable
            #nullable enable
            namespace {{defaultTestNamespace}};
            public partial class {{defaultTestClassName}}
            {
                /// <summary>
                /// Backing BindableProperty for the <see cref = "NullableInt"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty NullableIntProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("NullableInt", typeof(int? ), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial int? NullableInt { get => (int? )GetValue(NullableIntProperty); set => SetValue(NullableIntProperty, value); }

                /// <summary>
                /// Backing BindableProperty for the <see cref = "NullableDateTime"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty NullableDateTimeProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("NullableDateTime", typeof(System.DateTime? ), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial System.DateTime? NullableDateTime { get => (System.DateTime? )GetValue(NullableDateTimeProperty); set => SetValue(NullableDateTimeProperty, value); }

                /// <summary>
                /// Backing BindableProperty for the <see cref = "NullableBool"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty NullableBoolProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("NullableBool", typeof(bool? ), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial bool? NullableBool { get => (bool? )GetValue(NullableBoolProperty); set => SetValue(NullableBoolProperty, value); }
            }
            """;

		await VerifySourceGeneratorAsync(source, expectedGenerated);
	}

	[Fact]
	public async Task GenerateBindableProperty_ArrayTypes_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
            using CommunityToolkit.Maui;
            using Microsoft.Maui.Controls;

            namespace {{defaultTestNamespace}};

            public partial class {{defaultTestClassName}} : View
            {
                [BindableProperty]
                public partial string[] StringArray { get; set; }

                [BindableProperty]
                public partial int[,] MultiDimensionalArray { get; set; }

                [BindableProperty]
                public partial byte[][] JaggedArray { get; set; }
            }
            """;

		const string expectedGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
            // <auto-generated>
            // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
            #pragma warning disable
            #nullable enable
            namespace {{defaultTestNamespace}};
            public partial class {{defaultTestClassName}}
            {
                /// <summary>
                /// Backing BindableProperty for the <see cref = "StringArray"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty StringArrayProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("StringArray", typeof(string[]), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial string[] StringArray { get => (string[])GetValue(StringArrayProperty); set => SetValue(StringArrayProperty, value); }

                /// <summary>
                /// Backing BindableProperty for the <see cref = "MultiDimensionalArray"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty MultiDimensionalArrayProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("MultiDimensionalArray", typeof(int[, ]), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial int[, ] MultiDimensionalArray { get => (int[, ])GetValue(MultiDimensionalArrayProperty); set => SetValue(MultiDimensionalArrayProperty, value); }

                /// <summary>
                /// Backing BindableProperty for the <see cref = "JaggedArray"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty JaggedArrayProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("JaggedArray", typeof(byte[][]), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial byte[][] JaggedArray { get => (byte[][])GetValue(JaggedArrayProperty); set => SetValue(JaggedArrayProperty, value); }
            }
            """;

		await VerifySourceGeneratorAsync(source, expectedGenerated);
	}

	[Fact]
	public async Task GenerateBindableProperty_LongNamespaces_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
            using CommunityToolkit.Maui;
            using Microsoft.Maui.Controls;

            namespace Very.Long.Namespace.With.Many.Segments.TestNamespace;

            public partial class {{defaultTestClassName}} : View
            {
                [BindableProperty]
                public partial string Text { get; set; }
            }
            """;

		const string expectedGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
            // <auto-generated>
            // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
            #pragma warning disable
            #nullable enable
            namespace Very.Long.Namespace.With.Many.Segments.TestNamespace;
            public partial class {{defaultTestClassName}}
            {
                /// <summary>
                /// Backing BindableProperty for the <see cref = "Text"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty TextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Text", typeof(string), typeof(Very.Long.Namespace.With.Many.Segments.TestNamespace.TestView), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }
            }
            """;

		await VerifySourceGeneratorAsync(source, expectedGenerated);
	}

	[Fact]
	public async Task GenerateBindableProperty_GlobalNamespace_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
            using CommunityToolkit.Maui;
            using Microsoft.Maui.Controls;

            public partial class {{defaultTestClassName}} : View
            {
                [BindableProperty]
                public partial string Text { get; set; }
            }
            """;

		const string expectedGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
            // <auto-generated>
            // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
            #pragma warning disable
            #nullable enable
            public partial class {{defaultTestClassName}}
            {
                /// <summary>
                /// Backing BindableProperty for the <see cref = "Text"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty TextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Text", typeof(string), typeof(TestView), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }
            }
            """;

		await VerifySourceGeneratorAsync(source, expectedGenerated);
	}

	[Fact]
	public async Task GenerateBindableProperty_SpecialCharactersInPropertyName_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
            using CommunityToolkit.Maui;
            using Microsoft.Maui.Controls;

            namespace {{defaultTestNamespace}};

            public partial class {{defaultTestClassName}} : View
            {
                [BindableProperty()]
                public partial string Property_With_Underscores { get; set; }

                [BindableProperty()]
                public partial string Property123WithNumbers { get; set; }
            }
            """;

		const string expectedGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
            // <auto-generated>
            // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
            #pragma warning disable
            #nullable enable
            namespace {{defaultTestNamespace}};
            public partial class {{defaultTestClassName}}
            {
                /// <summary>
                /// Backing BindableProperty for the <see cref = "Property_With_Underscores"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty Property_With_UnderscoresProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Property_With_Underscores", typeof(string), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial string Property_With_Underscores { get => (string)GetValue(Property_With_UnderscoresProperty); set => SetValue(Property_With_UnderscoresProperty, value); }

                /// <summary>
                /// Backing BindableProperty for the <see cref = "Property123WithNumbers"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty Property123WithNumbersProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Property123WithNumbers", typeof(string), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial string Property123WithNumbers { get => (string)GetValue(Property123WithNumbersProperty); set => SetValue(Property123WithNumbersProperty, value); }
            }
            """;

		await VerifySourceGeneratorAsync(source, expectedGenerated);
	}

	[Fact]
	public async Task GenerateBindableProperty_WithComplexDefaultValues_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
            using CommunityToolkit.Maui;
            using Microsoft.Maui.Controls;

            namespace {{defaultTestNamespace}};

            public partial class {{defaultTestClassName}} : View
            {
                [BindableProperty(DefaultValue = true)]
                public partial bool IsEnabled { get; set; }

                [BindableProperty(DefaultValue = 3.14)]
                public partial double Pi { get; set; }

                [BindableProperty(DefaultValue = 'A')]
                public partial char Letter { get; set; }
            }
            """;

		const string expectedGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
            // <auto-generated>
            // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
            #pragma warning disable
            #nullable enable
            namespace {{defaultTestNamespace}};
            public partial class {{defaultTestClassName}}
            {
                /// <summary>
                /// Backing BindableProperty for the <see cref = "IsEnabled"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty IsEnabledProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("IsEnabled", typeof(bool), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), (bool)true, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial bool IsEnabled { get => (bool)GetValue(IsEnabledProperty); set => SetValue(IsEnabledProperty, value); }

                /// <summary>
                /// Backing BindableProperty for the <see cref = "Pi"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty PiProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Pi", typeof(double), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), (double)3.14, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial double Pi { get => (double)GetValue(PiProperty); set => SetValue(PiProperty, value); }

                /// <summary>
                /// Backing BindableProperty for the <see cref = "Letter"/> property.
                /// </summary>
                public static readonly global::Microsoft.Maui.Controls.BindableProperty LetterProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Letter", typeof(char), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), (char)'A', Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
                public partial char Letter { get => (char)GetValue(LetterProperty); set => SetValue(LetterProperty, value); }
            }
            """;

		await VerifySourceGeneratorAsync(source, expectedGenerated);
	}
}